/*!
\copyright  Copyright (c) 2017 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version    Part of 6.3.0
\file       vncl3020.c
\brief      Support for the vncl3020 proximity sensor
*/

#ifdef INCLUDE_PROXIMITY
#ifdef HAVE_VNCL3020
#include <bitserial_api.h>
#include <panic.h>
#include <pio.h>
#include <input_event_manager.h>

#include "../av_headset.h"
#include "../av_headset_log.h"
#include "../av_headset_proximity.h"
#include "vncl3020.h"

/*! \brief Returns the PIOs bank number.
    \param pio The pio.
*/
#define PIO2BANK(pio) ((uint16)((pio) / 32))
/*! \brief Returns the PIO bit position mask within a bank.
    \param pio The pio.
*/
#define PIO2MASK(pio) (1UL << ((pio) % 32))


/*! \brief Handle the proximity interrupt */
static void vncl3020InterruptHandler(Task task, MessageId id, Message msg)
{
    proximityTaskData *proximity = (proximityTaskData *) task;
    switch(id)
    {
        case MESSAGE_PIO_CHANGED:
        {
            const MessagePioChanged *mpc = (const MessagePioChanged *)msg;
            uint32 state = ((uint32)mpc->state16to31 << 16) + mpc->state;
            const proximityConfig *config = proximity->config;

            if (mpc->bank == PIO2BANK(config->pios.interrupt))
            {
                DEBUG_LOG("IO det prox changed!");
		   proximity->prox_state_flag = FALSE;
                if (~state & PIO2MASK(config->pios.interrupt))
                {
                    DEBUG_LOG("out_of_ear!");
		       proximity->prox_state_flag = TRUE;
                }
		proximity->detection_count = 0;
		
		MessageCancelAll(&proximity->task, LIGHT_SENSOR_VERIFY);
		MessageSendLater(&proximity->task, LIGHT_SENSOR_VERIFY, NULL, 200);
            }
        }
        break;
        case LIGHT_SENSOR_VERIFY:
        	{
                DEBUG_LOGF("LIGHT_SENSOR_VERIFY count = %d", proximity->detection_count);
			MessageCancelAll(&proximity->task, LIGHT_SENSOR_VERIFY);
			proximity->detection_count += 1;
			if(proximity->detection_count >= HY2751_DETC_COUNT)
			{
				/* Inform clients */
				if(proximity->prox_state_flag)
				{
					DEBUG_LOG("vncl3020InterruptHandler not in proximity");
					proximity->state->proximity = proximity_state_not_in_proximity;
					appTaskListMessageSendId(proximity->clients, PROXIMITY_MESSAGE_NOT_IN_PROXIMITY);
				}
				else
				{
					DEBUG_LOG("vncl3020InterruptHandler in proximity");
					proximity->state->proximity = proximity_state_in_proximity;
					appTaskListMessageSendId(proximity->clients, PROXIMITY_MESSAGE_IN_PROXIMITY);
				}
			}
			else
			{
				MessageSendLater(&proximity->task, LIGHT_SENSOR_VERIFY, NULL, 200);
			}
		}
        break;
        default:
        break;
    }
}

/*! \brief Enable the proximity sensor */
static void vncl3020Enable(const proximityConfig *config)
{
    uint16 bank;
    uint32 mask;

    DEBUG_LOG("vncl3020Enable");

    if (config->pios.on != VNCL3020_ON_PIO_UNUSED)
    {
        /* Setup power PIO then power-on the sensor */
        bank = PIO2BANK(config->pios.on);
        mask = PIO2MASK(config->pios.on);
        PanicNotZero(PioSetMapPins32Bank(bank, mask, mask));
        PanicNotZero(PioSetDir32Bank(bank, mask, mask));
        PanicNotZero(PioSet32Bank(bank, mask, mask));
    }

    /* Setup Interrupt as input with weak pull up */
    bank = PIO2BANK(config->pios.interrupt);
    mask = PIO2MASK(config->pios.interrupt);
    PanicNotZero(PioSetMapPins32Bank(bank, mask, mask));
    PanicNotZero(PioSetDir32Bank(bank, mask, 0));
    PanicNotZero(PioSet32Bank(bank, mask, mask));

}

/*! \brief Disable the proximity sensor */
static void vncl3020Disable(const proximityConfig *config)
{
    uint16 bank;
    uint32 mask;
    DEBUG_LOG("vncl3020Disable");

    /* Disable interrupt and set weak pull down */
    bank = PIO2BANK(config->pios.interrupt);
    mask = PIO2MASK(config->pios.interrupt);
    PanicNotZero(PioSet32Bank(bank, mask, 0));

    if (config->pios.on != VNCL3020_ON_PIO_UNUSED)
    {
        /* Power off the proximity sensor */
        PanicNotZero(PioSet32Bank(PIO2BANK(config->pios.on),
                                  PIO2MASK(config->pios.on),
                                  0));
    }
}

bool appProximityClientRegister(Task task)
{
    proximityTaskData *prox = appGetProximity();

    if (NULL == prox->clients)
    {
        const proximityConfig *config = appConfigProximity();
        prox->config = config;
        prox->state = PanicUnlessNew(proximityState);
        prox->state->proximity = proximity_state_unknown;
        prox->clients = appTaskListInit();

        vncl3020Enable(config);

        /* Register for interrupt events */
        prox->task.handler = vncl3020InterruptHandler;
        InputEventManagerRegisterTask(&prox->task, config->pios.interrupt);

    }

    /* Send initial message to client */
    switch (prox->state->proximity)
    {
        case proximity_state_in_proximity:
            MessageSend(task, PROXIMITY_MESSAGE_IN_PROXIMITY, NULL);
            break;
        case proximity_state_not_in_proximity:
            MessageSend(task, PROXIMITY_MESSAGE_NOT_IN_PROXIMITY, NULL);
            break;
        case proximity_state_unknown:
        default:
            /* The client will be informed after the first interrupt */
            break;
    }

    return appTaskListAddTask(prox->clients, task);
}

void appProximityClientUnregister(Task task)
{
    proximityTaskData *prox = appGetProximity();
    appTaskListRemoveTask(prox->clients, task);
    if (0 == appTaskListSize(prox->clients))
    {
        appTaskListDestroy(prox->clients);
        prox->clients = NULL;
        free(prox->state);
        prox->state = NULL;

        /* Unregister for interrupt events */
        InputEventManagerUnregisterTask(&prox->task, prox->config->pios.interrupt);

        /* Reset into lowest power mode in case the sensor is not powered off. */
        vncl3020Disable(prox->config);
    }
}

#endif /* HAVE_VNCL3020 */
#endif /* INCLUDE_PROXIMITY */
